/**
 * @(#)CoordinadorBundlesDepuracion 22/11/11
 *
 * Copyright (c) 2005-2010 PsiqueWare S.A. de C.V. All Rights Reserved.
 * This software is the proprietary information PsiqueWare S.A. de C.V.
 * Use is subject to license terms.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * <br/>Usuario : AngelElectrico
 * <br/>Creaci&oacute;n:22/11/11 - 06:09 PM PM
 * <br/>
 * <br/>
 * <br/><t5>Cambios :</t5>
 * <br/><b>Fecha | Autor  |  Cambios</b>
 * <ul>
 * <li>22/11/11 | <a href = "mailto:AngelElectrico@PsiqueWare.com">&Aacute;ngel El&eacute;ctrico</a> | Creaci&oacute;n del Objeto.</li>
 *
 * </ul>
 */

package com.psiqueware.alheli.i18n.depuracion;

import com.psiqueware.alheli.i18n.CoordinadorBundles;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <code>CoordinadorBundles</code> que manda a un archivo unificado los valores de todos los {@link ResourceBundle}
 * solicitados atraves de este.
 * <p/>
 * <br/>Paquete Original: com.psiqueware.alheli.i18n.depuracion
 * <br/>Nombre  Original: CoordinadorBundlesDepuracion
 * <br/>Oprima para ver : <a href="CoordinadorBundlesDepuracion.java.html"><i>C&oacute;digo Fuente</i> </a>
 *
 * @author <a href="mailto:AngelElectrico@PsiqueWare.com">&Aacute;ngel El&eacute;ctrico</a>
 * @version Revision: 1.1 22/11/11 06:09 PM
 */
public final class CoordinadorBundlesDepuracion implements CoordinadorBundles, Runnable
{

    /**
     * Segundos a esperar antes de salvar el documento.
     */
    private final AtomicInteger m_espera = new AtomicInteger();

    /**
     * Archivo donde se salvar&aacute;n todos los valores de todos los {@link ResourceBundle} solicitados.
     */
    private final File m_salvaValoresAqui;

    /**
     * {@link CoordinadorBundles} desde donde se obtendr&aacute;n los valores.
     */
    private final CoordinadorBundles m_coordinadorBundlesObtenValoresDe;

    /**
     * Valores obtenidos de los {@link ResourceBundle}.
     */
    private final Map<String, String> m_valores = new HashMap<String, String>();

    /**
     * Constructor de CoordinadorBundlesDepuracion.
     *
     * @param salvaValoresAqui Archivo donde se salvar&aacute;n todos los valores de todos los {@link ResourceBundle} solicitados.
     * @param coordinadorBundlesObtenValoresDe
     *                         el {@link CoordinadorBundles} desde donde se obtendr&aacute;n los valores.
     */
    public CoordinadorBundlesDepuracion(final File salvaValoresAqui, final CoordinadorBundles coordinadorBundlesObtenValoresDe)
    {
        this(salvaValoresAqui, coordinadorBundlesObtenValoresDe, false);
    }

    /**
     * Constructor de CoordinadorBundlesDepuracion.
     *
     * @param salvaValoresAqui Archivo donde se salvar&aacute;n todos los valores de todos los {@link ResourceBundle} solicitados.
     * @param coordinadorBundlesObtenValoresDe
     *                         el {@link CoordinadorBundles} desde donde se obtendr&aacute;n los valores.
     * @param cargarValores    Indica que si existe el archivo se debe intentar cargar los valores de este.
     */
    public CoordinadorBundlesDepuracion(final File salvaValoresAqui, final CoordinadorBundles coordinadorBundlesObtenValoresDe, final boolean cargarValores)
    {
        m_salvaValoresAqui = salvaValoresAqui;
        m_coordinadorBundlesObtenValoresDe = coordinadorBundlesObtenValoresDe;
        if (cargarValores) cargaValores();
    }


    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param contexto en el que se definir&aacute; el Bundle.
     * @param baseName nombre base del bundle.
     */
    @Override
    public void agregarResourceBundle(final String contexto, final String baseName)
    {
        m_coordinadorBundlesObtenValoresDe.agregarResourceBundle(contexto, baseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param contexto      en el que se definir&aacute; el Bundle.
     * @param classBaseName nombre base del bundle.
     */
    @Override
    public void agregarResourceBundle(final String contexto, final Class classBaseName)
    {
        m_coordinadorBundlesObtenValoresDe.agregarResourceBundle(contexto, classBaseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param baseName nombre base del bundle.
     */
    @Override
    public void agregarResourceBundle(final String baseName)
    {
        m_coordinadorBundlesObtenValoresDe.agregarResourceBundle(baseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada.
     *
     * @param classBaseName nombre base del bundle.
     */
    @Override
    public void agregarResourceBundle(final Class classBaseName)
    {
        m_coordinadorBundlesObtenValoresDe.agregarResourceBundle(classBaseName);
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param contexto en el que se definir&aacute; el Bundle.
     * @param baseName nombre base del bundle.
     * @param locale   {@link java.util.Locale} del bundle buscado.
     *
     * @return {@link ResourceBundle} buscado.
     */
    @Override
    public ResourceBundle agregaBuscaResourceBundle(final String contexto, final String baseName, final Locale locale)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.agregaBuscaResourceBundle(contexto, baseName, locale);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param contexto      en el que se definir&aacute; el Bundle.
     * @param classBaseName nombre base del bundle.
     * @param locale        {@link java.util.Locale} del bundle buscado.
     *
     * @return {@link ResourceBundle} buscado.
     */
    @Override
    public ResourceBundle agregaBuscaResourceBundle(final String contexto, final Class classBaseName, final Locale locale)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.agregaBuscaResourceBundle(contexto, classBaseName, locale);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param classBaseName nombre base del bundle.
     * @param locale        {@link java.util.Locale} del bundle buscado.
     *
     * @return {@link ResourceBundle} buscado.
     */
    @Override
    public ResourceBundle agregaBuscaResourceBundle(final Class classBaseName, final Locale locale)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.agregaBuscaResourceBundle(classBaseName, locale);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param baseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    @Override
    public ResourceBundle agregaBuscaResourceBundle(final String baseName)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.agregaBuscaResourceBundle(baseName);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Agrega un {@link ResourceBundle} a la colecci&oacute;n coordinada y lo devuelve.
     *
     * @param classBaseName nombre base del bundle.
     *
     * @return {@link ResourceBundle} buscado.
     */
    @Override
    public ResourceBundle agregaBuscaResourceBundle(final Class classBaseName)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.agregaBuscaResourceBundle(classBaseName);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Devuelve un ResourceBundle para un {@link java.util.Locale} y contexto espec&iacute;ficos.
     *
     * @param locale   localidad cuyos recursos se buscan.
     * @param contexto para el Bundle.
     *
     * @return {@link ResourceBundle}.
     */
    @Override
    public ResourceBundle getBundle(final Locale locale, final String contexto)
    {
        final ResourceBundle rb = m_coordinadorBundlesObtenValoresDe.getBundle(locale, contexto);
        agregaResourceBundleALeidos(rb);
        return rb;
    }

    /**
     * Agrega un {@link ResourceBundle} al conjunto de llaves de las cuales se lleva un seguimiento.
     *
     * @param rb a agregar.
     */
    private void agregaResourceBundleALeidos(final ResourceBundle rb)
    {
        final Enumeration<String> keys = rb.getKeys();
        while (keys.hasMoreElements())
        {
            final String key = keys.nextElement();
            final String value = rb.getString(key);

            m_valores.put(key, value);
        }

        if (m_espera.getAndIncrement() == 0)
        {
            new Thread(this, "CoordinadorBundlesDepuracionSalvando").start();
        }
        else
        {
            m_espera.set(5);
        }
    }

    /**
     * Devuelve un ResourceBundle para el {@link java.util.Locale} sin importarle el contexto de los recursos.
     *
     * @param locale localidad cuyos recursos se buscan.
     *
     * @return {@link ResourceBundle}.
     */
    @Override
    public ResourceBundle getBundle(final Locale locale)
    {
        return getBundle(locale, "");
    }

    /**
     * Devuelve un ResourceBundle sin importarle el contexto de los recursos y utilizando el {@link java.util.Locale} por defecto.
     *
     * @return {@link ResourceBundle}.
     */
    @Override
    public ResourceBundle getBundle()
    {
        return getBundle(Locale.getDefault(), "");
    }

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p/>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run()
    {
        m_espera.set(5);
        while (m_espera.getAndDecrement() > 0)
        {
            try
            {
                Thread.sleep(1000);
            }
            catch (InterruptedException ignored)
            {
            }
        }

        FileOutputStream fos = null;
        try
        {
            fos = new FileOutputStream(m_salvaValoresAqui);
            final Properties props = new Properties();
            props.putAll(m_valores);
            props.store(fos, "");
        }
        catch (IOException ignored)
        {
        }
        finally
        {
            if (null != fos)
            {
                try
                {
                    fos.close();
                }
                catch (IOException ignored)
                {
                }
            }
        }
        m_espera.set(0);
    }

    /**
     * Carga los valores del archivo
     */
    private void cargaValores()
    {
        if (!m_salvaValoresAqui.exists()) return;
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(m_salvaValoresAqui);
            final Properties props = new Properties();
            props.load(fis);
            final Set<String> keys = props.stringPropertyNames();
            for (final String key : keys)
            {
                final String value = props.getProperty(key);
                m_valores.put(key, value);
            }
        }
        catch (final IOException ignored)
        {

        }
        finally
        {
            if (null != fis)
            {
                try
                {
                    fis.close();
                }
                catch (IOException ignored)
                {
                }
            }
        }
    }
}

